﻿//Copyright (C) <2013>  <jonathan cleeve norton> All Rights Reserved 
//Contact jon.norton@fin-plus.co.uk website <http://www.fin-plus.co.uk/>
using System;
using System.Collections.Generic;
using System.Linq;
using FinPlusAssembler;
using FinPlusInterfaces;

namespace FinPlusCompCore
{
    public class FinPlusEquate: FinPlusComponent
    {
        [State(Test = true)]
        public override bool IsValid { get; set; }
        [State(Test = true)]
        public override string TestString { get { return Adapter.ToString(); } }
        public override IPods Adapter { get { return _adapter; } }
 
        private enum Params { LevelUpdate };
        private readonly IDictionary<string, IFinPlusComp> _conns;
        private readonly IDictionary<string, IPod> _index;
        private readonly IDictionary<string, string> _fields, _links, _expressions, _totals;
        private readonly IList<string> _keyLabels;
        private readonly IPods _adapter;
        private string _name;
        private int _id; 

        //construct
        public FinPlusEquate(IDictionary<string, IFinPlusComp> conn, string name, string[] keyLables, IDictionary<string, string> fields,
            IDictionary<string, string> links, IDictionary<string, string> expressions, IDictionary<string, string> totals)
        {
            _conns = conn;
            _name = name;
            _keyLabels = keyLables;
            _fields = fields;
            _links = links;
            _expressions = expressions;
            _totals = totals;
            _index = new Dictionary<string, IPod>();
            _adapter = IOC.New<IPods>();

            Load();
        }

        //common control interface
        public override void Dispose() 
        {
            foreach (var conn in _conns.Values)
                conn.Dispose();
        }

        //private
        public void Load()
        {
            foreach (var kvp in _conns)
                AddFields(kvp.Value.Adapter, kvp.Key);

            foreach (var kvp in _conns)
                AddLinks(kvp.Value.Adapter, kvp.Key);

            AddExpressions(_adapter);
            AddTotals(_adapter);

            IsValid = true;
        }

        private void AddFields(IPods pods, string name)
        {
            try
            {
                if (_fields == null) 
                    return;

                foreach (var f in _fields.Where(s => s.Value.Equals(name)))
                    foreach (var pod in pods.Values)
                        AddField(pod, f.Key);
            }
            catch (Exception e)
            {
                IsValid = Level.Error.Log(Id, Config, e.Message, e);
            }
        }

        private void AddField(IPod pod, string name)
        {
            try
            {
                var val = pod.Get(name);
                if (val == null) 
                    return;

                var f = GetPod(pod, name);
                val.Index = -1;
                f.TryAdd(val); 
            }
            catch (Exception e)
            {
                IsValid = Level.Error.Log(Id, Config, e.Message, e);
            }
        }

        private void AddLinks(IPods pods, string name)
        {
            try
            {
                if (_links == null) 
                    return;

                foreach (var f in _links.Where(s => s.Key.Split('.')[0].Equals(name)))
                    foreach (var pod in pods.Values)
                        AddLink(pod, f.Value, f.Key);
            }
            catch (Exception e)
            {
                IsValid = Level.Error.Log(Id, Config, e.Message, e);
            }
        }

        private void AddLink(IPod pod, string left, string right)
        {
            try
            {
                string[] l = left.Split('.'), r = right.Split('.');
                var label = pod[l[1]].ToString();
                foreach (var model in _adapter.ToList(l[0], label))
                    model.TryAdd(pod.Get<double>(r[1], 0.0));
            }
            catch (Exception e)
            {
                IsValid = Level.Error.Log(Id, Config, e.Message, e);
            }
        }

        private void AddExpressions(IPods pods)
        {
            try
            {
                if (_expressions == null) 
                    return;

                foreach (var f in _expressions)
                    foreach (var pod in pods.Values)
                        AddExpression(pod, f.Key, f.Value);
            }
            catch (Exception e)
            {
                IsValid = Level.Error.Log(Id, Config, e.Message, e);
            }
        }

        private void AddExpression(IPod pod, string name, string expression)
        {
            try
            {
                var bean = GetPod(pod, name).Add(name, 0.0);
                bean.Evaluate(pod, expression);
            }
            catch (Exception e)
            {
                IsValid = Level.Error.Log(Id, Config, e.Message, e);
            }
        }

        private void AddTotals(IPods pods)
        {
            try
            {
                if (_totals == null) 
                    return;
                
                foreach (var total in _totals)
                    AddTotal(pods, total.Value, total.Key);
            }
            catch (Exception e)
            {
                IsValid = Level.Error.Log(Id, Config, e.Message, e);
            }
        }

        private void AddTotal(IPods pods, string left, string right)
        {
            try
            {
                string[] l = left.Split('.'), r = right.Split('.');
                foreach (var pod in pods.Values.Where(f => f[l[0]].Equals(l[1])))
                {
                    var beans = pod.ToList(r[1], false);
                    
                    if (beans.Count() < 1)
                        continue;
                    
                    var bean = beans.First();
                    var link = pod.Where(i => i.Name.Equals(r[0])).First().ToString();
                    var list = new List<IBean>();
                    
                    foreach (var pod2 in pods.Values)
                        if (pod2[r[0]].Equals(link) && !pod2.Equals(pod))
                            list.AddRange(pod2.ToList(r[1], false));
                    
                    bean.Total(list);
                }
            }
            catch (Exception e)
            {
                IsValid = Level.Error.Log(Id, Config, e.Message, e);
            }
        }

        private IPod GetPod(IPod pod, string name)
        {
            var key = string.Empty;
            foreach (var keyLabel in _keyLabels)
                key += pod[keyLabel] + "|";

            key = key.TrimEnd('|');
                    
            if (_index.ContainsKey(key)) 
                return _index[key];

            return AddPod(pod, key);
        }

        private IPod AddPod(IPod pod, string key)
        {
            _id++;
            IPod f;
            _index[key] = f = IOC.New<IPod>(_name, _id.ToString(), User, _id);
            foreach (var i in pod)
                if (_keyLabels.Contains(i.Name))
                    f.TryAdd(i);

            _adapter.Add(f);

            return f;
        }
    }
}
